
/*
用按键清除nvs中的配网参数，用led显示联网状态
*/

/*
用GPIO监测按键状态，持续低电平3秒则删除wifi的配网参数，重启，恢复出厂设置，
原理是：配置button脚为边沿触发中断，检测到下降沿说明有按键开始按下启动软件定时器，
定时器每10ms将计时标志累加一次，再检测到上升沿说明按键抬起。
校验从按下到抬起的时间，超过一定时间区分长短按，同时关闭定时器，清零计时标志。核心代码如下：
*/

/*用LED的状态来代表配网的状态
灯的不同状态代表不同的配网状态，
设定：慢闪代表未联网，快闪代表正在联网，长亮代表网络已连接。
这里会用到ESP32的系统定时器，使用方法也比较简单，创建定时器，开启或者关闭定时器。
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "driver/gpio.h"
#include "includes.h"
#include "esp_timer.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/timers.h"

static const char *TAG = "WifiStatus_LED_KEY";

//定时器回调里实现灯的闪烁
static void periodic_timer_callback(void *arg)
{
    static uint8_t s_LEDToggle;
    s_LEDToggle = ~s_LEDToggle;
    if (s_LEDToggle)
    {
        gpio_set_level(SmartconfigLed_GPIO, 1);
    }
    else
    {
        gpio_set_level(SmartconfigLed_GPIO, 0);
    }
}

//根据当前状态，决定如何闪烁
static void Handle_Smartconfig_LED_Status(Wifi_Status_t WifiState)
{
    static uint8_t s_WifiDisconnectNotice;
    static esp_timer_handle_t Smartconfig_LED_timer;
    switch (WifiState)
    {
    case wifi_discononet:
        //没有连上网的状态下创建并开启软件定时器，并且确保只创建一次，不然会报错
        if (s_WifiDisconnectNotice == 0)
        {
            esp_timer_create_args_t periodic_timer_args = {
                .callback = &periodic_timer_callback,
                /* name is optional, but may help identify the timer when debugging */
                .name = "periodic"};
            ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &Smartconfig_LED_timer));
            ESP_ERROR_CHECK(esp_timer_start_periodic(Smartconfig_LED_timer, ConnectStatusInterval));
            s_WifiDisconnectNotice = 1;
        }
        break;
    case wifi_connecting:
        //正在联网的时候LED快闪
        ESP_ERROR_CHECK(esp_timer_stop(Smartconfig_LED_timer));
        ESP_ERROR_CHECK(esp_timer_start_periodic(Smartconfig_LED_timer, DisconnectStatusInterval));
        break;
    case wifi_connected:
        //连上网后LED常亮，并注销软件定时器，减少消耗
        gpio_set_level(SmartconfigLed_GPIO, 1);
        if (s_WifiDisconnectNotice == 1)
        {
            ESP_ERROR_CHECK(esp_timer_stop(Smartconfig_LED_timer));
            ESP_ERROR_CHECK(esp_timer_delete(Smartconfig_LED_timer));
            s_WifiDisconnectNotice = 0;
        }
        break;
    default:
        break;
    }
}

//LED管脚初始化-------------------------
void WifiStatus_LED_Init(void)
{
    gpio_config_t smartconfig_IO_conf;
    smartconfig_IO_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    smartconfig_IO_conf.mode = GPIO_MODE_OUTPUT;
    smartconfig_IO_conf.pin_bit_mask = 1 << SmartconfigLed_GPIO;
    smartconfig_IO_conf.pull_down_en = 0;
    smartconfig_IO_conf.pull_up_en = 0;
    gpio_config(&smartconfig_IO_conf);
    gpio_set_level(SmartconfigLed_GPIO, 0);
}

//通知网络未连接
void Delegate_Wifi_Disconnect_Status(Wifi_Status_t WifiState)
{
    ESP_LOGI(TAG, "Delegate wifi is disconnect\n");
    Handle_Smartconfig_LED_Status(WifiState);
}
//通知网络已连接
void Delegate_Wifi_Connected_Status(Wifi_Status_t WifiState)
{
    ESP_LOGI(TAG, "Delegate wifi has connected\n");
    Handle_Smartconfig_LED_Status(WifiState);
}
//通知网络正在连接中
void Delegate_Wifi_Connecting_Status(Wifi_Status_t WifiState)
{
    ESP_LOGI(TAG, "Delegate wifi is connecting\n");
    Handle_Smartconfig_LED_Status(WifiState);
}

//以下是按键检测----------------------------------------------------------------
//周期定时器句柄
static TimerHandle_t AutoReloadTimer_Handle;
//按键计时标志
static uint16_t SmartlinkKeyCount;
//GPIO事件队列
static xQueueHandle gpio_evt_queue = NULL;
//按键中断回调
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
    uint32_t gpio_num = (uint32_t)arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
//软件定时器回调
static void AutoReloadCallback(TimerHandle_t xTimer)
{
    SmartlinkKeyCount++;
}

//智能配网按键GPIO检测任务---------------------
//注意xTimerCreate的定时设定怎么不对呢？
static void Smartlink_gpio_task(void *arg)
{
    uint32_t io_num;
    bool Smartconfig_button_press = false;
    bool Smartconfig_button_uplift = false;
    static uint32_t ButtonPressTime = 0;

    while (1)
    {
        if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
        {
            //判断按键
            if (gpio_get_level(io_num) == 0) //按键按下
            {
                Smartconfig_button_press = true;
                SmartlinkKeyCount = 0;
                ButtonPressTime = 0;
                //启动10ms定时器
                AutoReloadTimer_Handle = xTimerCreate("AutoReloadTimer", 100 / portTICK_PERIOD_MS, pdTRUE, (void *)1, AutoReloadCallback);
                xTimerStart(AutoReloadTimer_Handle, 0);
                ButtonPressTime = SmartlinkKeyCount;
            }
            else if (Smartconfig_button_press) //按键松开
            {
                Smartconfig_button_uplift = true;
                ButtonPressTime = SmartlinkKeyCount - ButtonPressTime;
                xTimerStop(AutoReloadTimer_Handle, 0);
                SmartlinkKeyCount = 0;
            }
            if (Smartconfig_button_press & Smartconfig_button_uplift) //按键经历了按下到放开的过程，校验时间
            {
                Smartconfig_button_press = false;
                Smartconfig_button_uplift = false;
                if (ButtonPressTime > 500) //按下持续时间超过5秒
                {
                    //按键长按，清除配网信息并重启
                    printf("key long press, ButtonPressTime=%d\n", ButtonPressTime);
                    ESP_ERROR_CHECK(nvs_flash_erase());
                    ESP_LOGI(TAG, "Set Restart now.\n");
                    esp_restart();
                }
                else if (ButtonPressTime > 20) //按下时间超过200ms才为短按
                {
                    printf("key short press\n");
                }
                SmartlinkKeyCount = 0;
                ButtonPressTime = 0;
            }
        }
    }
    printf("Smartlink_gpio_task exit\n");
}

//智能配网按键初始化-------------------------------
void Smartconfig_Button_Init(void)
{
    //配置配网按键
    gpio_config_t button_io_conf;
    button_io_conf.intr_type = GPIO_PIN_INTR_ANYEDGE; //上下跳沿都中断
    button_io_conf.mode = GPIO_MODE_INPUT;
    button_io_conf.pin_bit_mask = 1ULL << Button_GPIO;
    button_io_conf.pull_up_en = 1;
    button_io_conf.pull_down_en = 0;
    gpio_config(&button_io_conf);
    ESP_LOGI(TAG, "Smartconfig button Init Ok\n");

    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //start gpio task
    xTaskCreate(Smartlink_gpio_task, "smartlink_gpio_task", 2048, NULL, 10, NULL);

    //install gpio isr service
    gpio_install_isr_service(0);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(Button_GPIO, gpio_isr_handler, (void *)Button_GPIO);
}
